package handlers

import (
	"encoding/json"
	"log"
	"net/http"
	"sync"
	"time"

	"gin-chat-server/common"
	"gin-chat-server/dto"
	"gin-chat-server/models"
	service "gin-chat-server/services"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true // 在生产环境中应该检查origin
	},
}

type Client struct {
	Conn   *websocket.Conn
	UserID string
	RoomID string
	Send   chan []byte
}

type Hub struct {
	Clients     map[string]*Client            // userID -> Client
	Rooms       map[string]map[string]*Client // roomID -> map[userID]Client
	Register    chan *Client
	Unregister  chan *Client
	Broadcast   chan []byte
	mu          sync.RWMutex
	chatService *service.ChatService
}

var hub = &Hub{
	Clients:     make(map[string]*Client),
	Rooms:       make(map[string]map[string]*Client),
	Register:    make(chan *Client),
	Unregister:  make(chan *Client),
	Broadcast:   make(chan []byte),
	chatService: service.NewChatService(),
}

func (h *Hub) Run() {
	for {
		select {
		case client := <-h.Register:
			h.mu.Lock()
			h.Clients[client.UserID] = client
			if client.RoomID != "" {
				if h.Rooms[client.RoomID] == nil {
					h.Rooms[client.RoomID] = make(map[string]*Client)
				}
				h.Rooms[client.RoomID][client.UserID] = client

				// 通知其他用户有新用户加入
				h.broadcastToRoom(client.RoomID, NewMessage("user_joined", map[string]interface{}{
					"user_id": client.UserID,
					"room_id": client.RoomID,
				}))
			}
			h.mu.Unlock()

		case client := <-h.Unregister:
			h.mu.Lock()
			if _, ok := h.Clients[client.UserID]; ok {
				delete(h.Clients, client.UserID)
				close(client.Send)

				if client.RoomID != "" {
					if roomClients, exists := h.Rooms[client.RoomID]; exists {
						delete(roomClients, client.UserID)
						if len(roomClients) == 0 {
							delete(h.Rooms, client.RoomID)
						} else {
							// 通知其他用户有用户离开
							h.broadcastToRoom(client.RoomID, NewMessage("user_left", map[string]interface{}{
								"user_id": client.UserID,
								"room_id": client.RoomID,
							}))
						}
					}
				}
			}
			h.mu.Unlock()

		case message := <-h.Broadcast:
			h.mu.RLock()
			for _, client := range h.Clients {
				select {
				case client.Send <- message:
				default:
					close(client.Send)
					delete(h.Clients, client.UserID)
				}
			}
			h.mu.RUnlock()
		}
	}
}

func (h *Hub) broadcastToRoom(roomID string, message []byte) {
	h.mu.RLock()
	defer h.mu.RUnlock()

	if roomClients, exists := h.Rooms[roomID]; exists {
		for _, client := range roomClients {
			select {
			case client.Send <- message:
			default:
				close(client.Send)
				delete(roomClients, client.UserID)
				if len(roomClients) == 0 {
					delete(h.Rooms, roomID)
				}
			}
		}
	}
}

func (h *Hub) SendMessageToRoom(roomID string, message []byte) {
	h.broadcastToRoom(roomID, message)
}

func (h *Hub) SendMessageToUser(userID string, message []byte) {
	h.mu.RLock()
	defer h.mu.RUnlock()

	if client, exists := h.Clients[userID]; exists {
		select {
		case client.Send <- message:
		default:
			close(client.Send)
			delete(h.Clients, userID)
		}
	}
}

type WebSocketMessage struct {
	Type string      `json:"type"`
	Data interface{} `json:"data"`
}

func NewMessage(messageType string, data interface{}) []byte {
	msg := WebSocketMessage{
		Type: messageType,
		Data: data,
	}
	jsonMsg, _ := json.Marshal(msg)
	return jsonMsg
}

func WebSocketHandler(c *gin.Context) {
	userID := common.GetCurrentUserID(c)
	if userID == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
		return
	}

	// 升级HTTP连接为WebSocket
	conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		log.Println("WebSocket upgrade error:", err)
		return
	}
	defer conn.Close()

	client := &Client{
		Conn:   conn,
		UserID: userID,
		Send:   make(chan []byte, 256),
	}

	// 注册客户端
	hub.Register <- client

	// 启动写入协程
	go client.writePump()

	// 启动读取协程
	client.readPump()
}

func (c *Client) readPump() {
	defer func() {
		hub.Unregister <- c
		c.Conn.Close()
	}()

	c.Conn.SetReadLimit(512 * 1024) // 512KB
	c.Conn.SetReadDeadline(time.Now().Add(60 * time.Second))
	c.Conn.SetPongHandler(func(string) error {
		c.Conn.SetReadDeadline(time.Now().Add(60 * time.Second))
		return nil
	})

	for {
		_, message, err := c.Conn.ReadMessage()
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				log.Printf("error: %v", err)
			}
			break
		}

		// 处理接收到的消息
		c.handleMessage(message)
	}
}

func (c *Client) writePump() {
	ticker := time.NewTicker(50 * time.Second)
	defer func() {
		ticker.Stop()
		c.Conn.Close()
	}()

	for {
		select {
		case message, ok := <-c.Send:
			c.Conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
			if !ok {
				c.Conn.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}

			w, err := c.Conn.NextWriter(websocket.TextMessage)
			if err != nil {
				return
			}
			w.Write(message)

			if err := w.Close(); err != nil {
				return
			}

		case <-ticker.C:
			c.Conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
			if err := c.Conn.WriteMessage(websocket.PingMessage, nil); err != nil {
				return
			}
		}
	}
}

func (c *Client) handleMessage(message []byte) {
	var wsMsg WebSocketMessage
	if err := json.Unmarshal(message, &wsMsg); err != nil {
		log.Println("WebSocket message unmarshal error:", err)
		return
	}

	switch wsMsg.Type {
	case "join_room":
		if roomID, ok := wsMsg.Data.(string); ok {
			c.joinRoom(roomID)
		}

	case "leave_room":
		if roomID, ok := wsMsg.Data.(string); ok {
			c.leaveRoom(roomID)
		}

	case "send_message":
		if data, ok := wsMsg.Data.(map[string]interface{}); ok {
			c.sendMessage(data)
		}

	case "typing_start":
		if data, ok := wsMsg.Data.(map[string]interface{}); ok {
			c.handleTyping(data, true)
		}

	case "typing_end":
		if data, ok := wsMsg.Data.(map[string]interface{}); ok {
			c.handleTyping(data, false)
		}

	case "mark_read":
		if messageID, ok := wsMsg.Data.(string); ok {
			c.markMessageAsRead(messageID)
		}
	}
}
func (c *Client) joinRoom(roomID string) {
	// 检查用户是否有权限加入房间
	err := hub.chatService.JoinRoom(roomID, c.UserID)
	if err != nil {
		c.Send <- NewMessage("error", map[string]interface{}{
			"message": "Failed to join room: " + err.Error(),
		})
		return
	}

	// 更新客户端的房间ID
	hub.mu.Lock()
	if c.RoomID != "" {
		// 从之前的房间移除
		if roomClients, exists := hub.Rooms[c.RoomID]; exists {
			delete(roomClients, c.UserID)
			if len(roomClients) == 0 {
				delete(hub.Rooms, c.RoomID)
			}
		}
	}

	c.RoomID = roomID
	if hub.Rooms[roomID] == nil {
		hub.Rooms[roomID] = make(map[string]*Client)
	}
	hub.Rooms[roomID][c.UserID] = c
	hub.mu.Unlock()

	// 通知房间内其他用户
	hub.broadcastToRoom(roomID, NewMessage("user_joined", map[string]interface{}{
		"user_id": c.UserID,
		"room_id": roomID,
	}))

	// 发送成功消息
	c.Send <- NewMessage("room_joined", map[string]interface{}{
		"room_id": roomID,
	})
}
func (c *Client) leaveRoom(roomID string) {
	hub.mu.Lock()
	defer hub.mu.Unlock()

	if roomClients, exists := hub.Rooms[roomID]; exists {
		delete(roomClients, c.UserID)
		if len(roomClients) == 0 {
			delete(hub.Rooms, roomID)
		} else {
			// 通知房间内其他用户
			hub.broadcastToRoom(roomID, NewMessage("user_left", map[string]interface{}{
				"user_id": c.UserID,
				"room_id": roomID,
			}))
		}
	}

	if c.RoomID == roomID {
		c.RoomID = ""
	}
}

func (c *Client) sendMessage(data map[string]interface{}) {
	roomID, _ := data["room_id"].(string)
	content, _ := data["content"].(string)
	messageType, _ := data["message_type"].(string)

	if roomID == "" || content == "" {
		c.Send <- NewMessage("error", map[string]interface{}{
			"message": "room_id and content are required",
		})
		return
	}

	if messageType == "" {
		messageType = "text"
	}

	// 保存消息到数据库
	message, err := hub.chatService.SendMessage(roomID, c.UserID, &dto.SendMessageRequest{
		Content: content,
		Type:    messageType,
	})

	if err != nil {
		c.Send <- NewMessage("error", map[string]interface{}{
			"message": "Failed to send message: " + err.Error(),
		})
		return
	}

	// 广播消息到房间内所有用户
	messageData := map[string]interface{}{
		"id":         message.ID,
		"room_id":    message.RoomID,
		"user_id":    message.UserID,
		"content":    message.Content,
		"type":       message.Type,
		"status":     message.Status,
		"created_at": message.CreatedAt,
	}

	if message.ReplyTo != nil {
		messageData["reply_to"] = *message.ReplyTo
	}
	if message.FileURL != nil {
		messageData["file_url"] = *message.FileURL
	}
	if message.FileName != nil {
		messageData["file_name"] = *message.FileName
	}
	if message.FileSize != nil {
		messageData["file_size"] = *message.FileSize
	}

	hub.broadcastToRoom(roomID, NewMessage("message", messageData))
}

func (c *Client) handleTyping(data map[string]interface{}, isTyping bool) {
	roomID, _ := data["room_id"].(string)
	if roomID == "" {
		return
	}

	messageType := "typing_start"
	if !isTyping {
		messageType = "typing_end"
	}

	hub.broadcastToRoom(roomID, NewMessage(messageType, map[string]interface{}{
		"user_id": c.UserID,
		"room_id": roomID,
	}))
}

func (c *Client) markMessageAsRead(messageID string) {
	err := hub.chatService.MarkMessageAsRead(messageID, c.UserID)
	if err != nil {
		c.Send <- NewMessage("error", map[string]interface{}{
			"message": "Failed to mark message as read: " + err.Error(),
		})
		return
	}

	// 通知消息发送者
	var message models.Message
	if err := common.GetDB().First(&message, "id = ?", messageID).Error; err == nil {
		hub.SendMessageToUser(message.UserID, NewMessage("message_read", map[string]interface{}{
			"message_id": messageID,
			"user_id":    c.UserID,
		}))
	}
}

// 初始化WebSocket Hub
func InitWebSocket() {
	go hub.Run()
}
